﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App1AppHandler;
using System.Data.SqlClient;
using System.IO;
using System.Xml;
using System.Net.Sockets;
using Reborn_WorldServer;
using System.Timers;

namespace Reborn_Server.App1AppHandler
{
    public class FriendsManager
    {
        private Character MyCharacter;
        private List<FriendInfo> Friends = new List<FriendInfo>();

        private Timer timerFindFriendsPrimaryTimeout = new Timer(1000 * 6);
        private bool waitingOnResponse = false;

        public FriendsManager(Character character)
        {
            timerFindFriendsPrimaryTimeout.AutoReset = false;
            timerFindFriendsPrimaryTimeout.Elapsed += new ElapsedEventHandler(timerFindFriendsPrimaryTimeout_Elapsed);

            MyCharacter = character;

            //Retrieve Friends List
            lock (Friends)
            {
                //LOAD ITEM IDs
                SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
                SqlCommand SQLCom = new SqlCommand("Characters_Friends_Select", MyCon);
                SQLCom.CommandType = System.Data.CommandType.StoredProcedure;
                SQLCom.Parameters.AddWithValue("@CharacterID", character.ID);

                MyCon.Open();
                SqlDataReader SDR = SQLCom.ExecuteReader();

                while (SDR.Read())
                {
                    FriendInfo friendInfo = new FriendInfo();
                    friendInfo.ID = int.Parse(SDR["FriendID"].ToString());
                    friendInfo.Name = SDR["Name"].ToString();
                    friendInfo.Location = "Offline";
                    friendInfo.Instance = "Offline";
                    friendInfo.NoSDFlag = bool.Parse(SDR["NoSDFlag"].ToString());

                    Friends.Add(friendInfo);
                }
                SDR.Close();
                SDR.Dispose();
                MyCon.Close();
                MyCon.Dispose();
            }
        }

        private void timerFindFriendsPrimaryTimeout_Elapsed(Object sender, ElapsedEventArgs e)
        {
            processFriendDataFromPrimary(null);
        }

        //Scans this characters friends and sends a message if this player is in their list.
        public void friendOnline(int characterId)
        {
            lock (Friends)
            {
                foreach (FriendInfo fi in Friends)
                {
                    if (fi.ID == characterId)
                    {
                        MyCharacter.SendMessage(fi.Name + " is now online", "server");
                        break;
                    }
                }
            }
        }

        public bool hasNoSDFlag(int characterId)
        {
            lock (Friends)
            {
                foreach (FriendInfo fi in Friends)
                {
                    if (fi.ID == characterId)
                        return fi.NoSDFlag;
                }
            }
            return false;
        }

        //Scans this characters friends and sends a message if this player is in their list.
        public void friendOffline(int characterId)
        {
            lock (Friends)
            {
                foreach (FriendInfo fi in Friends)
                {
                    if (fi.ID == characterId)
                    {
                        MyCharacter.SendMessage(fi.Name + " is now offline", "server");
                        break;
                    }
                }
            }
        }

        //First checks this server for any friends online, then sends remaining friends list to primary server
        public void getFriendData()
        {
            List<FriendInfo> friendsToRequestFromPrimary = new List<FriendInfo>();
            lock (Friends)
            {
                //We have a pending request. Data is already on the way.
                if (waitingOnResponse == true)
                    return;

                //Check this server for friends first
                foreach (FriendInfo fi in Friends)
                {
                    fi.Instance = "Offline";
                    fi.Location = "Offline";

                    Character friend = Managers.characterManager.getCharacter(fi.ID);
                    if (friend == null)
                        friendsToRequestFromPrimary.Add(fi);
                    else
                    {
                        string mapName = Managers.GameHandler.GetMapName(friend.MapID);
                        string instanceName = Managers.GameHandler.GetInstanceName(friend.Instance);

                        fi.Name = friend.Name;
                        fi.Instance = instanceName;
                        fi.Location = mapName;
                    }
                }
                //All friends were found on this server, no need to query primary
                if (friendsToRequestFromPrimary.Count == 0)
                {
                    sendFriendDataToClient();
                    return;
                }
                //Set this inside the lock
                else
                    waitingOnResponse = true;
            }

            //Generate a list of friend IDs to send to primary to query servers and see if they are online
            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("findfriendsWS");
            xmldoc.WriteAttributeString("friendsofId", MyCharacter.ID.ToString());

            foreach (FriendInfo fi in friendsToRequestFromPrimary)
            {
                xmldoc.WriteStartElement("friend");
                xmldoc.WriteAttributeString("id", fi.ID.ToString());
                xmldoc.WriteEndElement();
            }
            xmldoc.WriteEndElement();

            xmldoc.Flush();
            xmldoc.Close();

            stringWriter.Flush();
            string xmlMessage = stringWriter.ToString();

            Managers.BeginSendToPrimary(xmlMessage);
            timerFindFriendsPrimaryTimeout.Start();
        }

        public void processFriendDataFromPrimary(string xmlData)
        {
            List<FriendInfo> locatedFriends = new List<FriendInfo>();

            lock (Friends)
            {
                //We are not expecting a responce from the primary (maybe it was to slow and timed out)
                //and we've already send the list to the client, so do nothing with this data.
                if (waitingOnResponse == false)
                    return;
                
                waitingOnResponse = false;
                timerFindFriendsPrimaryTimeout.Stop();

                if (xmlData != "" && xmlData != null)
                {
                    //Convert the XML data to a list so we can replace the data in the friends list.
                    StringReader sr = new StringReader(xmlData);
                    XmlTextReader xmlReader = new XmlTextReader(sr);
                    while (xmlReader.Read())
                    {
                        if (xmlReader.Name == "frienddata")
                        {
                            FriendInfo fi = new FriendInfo();
                            fi.ID = int.Parse(xmlReader.GetAttribute("id").ToString());
                            fi.Name = xmlReader.GetAttribute("name").ToString();
                            fi.Location = Managers.GameHandler.GetMapName(int.Parse(xmlReader.GetAttribute("mapid").ToString()));
                            fi.Instance = Managers.GameHandler.GetInstanceName(int.Parse(xmlReader.GetAttribute("instance").ToString()));

                            locatedFriends.Add(fi);
                        }
                    }
                }

                //No additional friends were found online (or primary request timed out) just send what we have.
                if (locatedFriends == null || locatedFriends.Count == 0)
                {
                    sendFriendDataToClient();
                    return;
                }

                foreach (FriendInfo fi in locatedFriends)
                {
                    foreach (FriendInfo fi2 in Friends)
                    {
                        if (fi.ID == fi2.ID)
                        {
                            fi2.Instance = fi.Instance;
                            fi2.Location = fi.Location;
                            fi2.Name = fi.Name;
                        }
                    }
                }

                sendFriendDataToClient();
            }
        }

        public void DeleteFriend(int FriendID)
        {
            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Friends_Delete", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

            SQLCom.Parameters.AddWithValue("@CharacterID", MyCharacter.ID);
            SQLCom.Parameters.AddWithValue("@FriendID", FriendID);

            MyCon.Open();
            SQLCom.ExecuteNonQuery();
            MyCon.Close();
            MyCon.Dispose();

            lock (Friends)
            {
                foreach (FriendInfo fi in Friends)
                {
                    if (fi.ID == FriendID)
                    {
                        Friends.Remove(fi);
                        break;
                    }
                }
            }
        }
        public void AddFriend(string Name)
        {
            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Friends_Insert", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

            SQLCom.Parameters.AddWithValue("@CharacterID", MyCharacter.ID);
            SQLCom.Parameters.AddWithValue("@Name", Name);

            SQLCom.Parameters.Add("@PlayersName", System.Data.SqlDbType.VarChar, 50);
            SQLCom.Parameters["@PlayersName"].Direction = System.Data.ParameterDirection.Output;

            SQLCom.Parameters.Add("@Return_Value", System.Data.SqlDbType.Int);
            SQLCom.Parameters["@Return_Value"].Direction = System.Data.ParameterDirection.ReturnValue;

            MyCon.Open();
            SQLCom.ExecuteNonQuery();

            int FriendID = int.Parse(SQLCom.Parameters["@Return_Value"].Value.ToString());
            string FriendName = SQLCom.Parameters["@PlayersName"].Value.ToString();

            MyCon.Close();
            MyCon.Dispose();

            FriendInfo newFriend = new FriendInfo();

            if (FriendID == -1)
            {
                Message msg = new Message();
                msg.Type = "server";
                msg.CreatureID = MyCharacter.CreatureID;
                msg.Text = "No characters named " + Name;

                MyCharacter.PlaySound("Ding", MyCharacter.MapX, MyCharacter.MapY);
                MyCharacter.AddMessage(msg);

                return;
            }
            else if (FriendID == -2)
            {
                Message msg = new Message();
                msg.Type = "server";
                msg.CreatureID = MyCharacter.CreatureID;
                msg.Text = Name + " is already on your friends list.";

                MyCharacter.PlaySound("Ding", MyCharacter.MapX, MyCharacter.MapY);
                MyCharacter.AddMessage(msg);

                return;
            }
            else
            {
                newFriend.ID = FriendID;
                newFriend.Name = FriendName;
                newFriend.Location = "Offline";
                newFriend.Instance = "Offline";

                Friends.Add(newFriend);
            }

            //We added a new friend, so now we need to see if they are online, refresh the data
            getFriendData();
        }

        public void updateFriendNoSDFlag(int friendId, bool noSdFlag)
        {
            lock (Friends)
            {
                foreach (FriendInfo f in Friends)
                {
                    if (f.ID == friendId)
                        f.NoSDFlag = noSdFlag;
                }
            }

            SqlConnection MyCon = new SqlConnection(Managers.DBConnectionString);
            SqlCommand SQLCom = new SqlCommand("Characters_Friends_UpdateNoSDFlag", MyCon);
            SQLCom.CommandType = System.Data.CommandType.StoredProcedure;

            SQLCom.Parameters.AddWithValue("@CharacterID", MyCharacter.ID);
            SQLCom.Parameters.AddWithValue("@FriendID", friendId);
            SQLCom.Parameters.AddWithValue("@NoSDFlag", noSdFlag);

            MyCon.Open();
            SQLCom.ExecuteNonQuery();
            MyCon.Close();
            MyCon.Dispose();
        }

        private void sendFriendDataToClient()
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("friends");

            lock (Friends)
            {
                foreach (FriendInfo fi in Friends)
                    xmldoc.WriteRaw(fi.toXML());
            }

            xmldoc.WriteEndElement();
            xmldoc.Flush();

            xmldoc.Close();

            stringWriter.Flush();

            result = stringWriter.ToString();

            try
            {
                MyCharacter.ClientSocket.socket.BeginSend(result);
            }
            catch { }
        }
        
    }
    class FriendInfo
    {
        public int ID = -1;
        public string Name = "";
        public bool NoSDFlag = false;
        public string Location = "Offline";
        public string Instance = "Offline";

        public string toXML()
        {
            string result = "";

            StringWriter stringWriter = new StringWriter();
            XmlTextWriter xmldoc = new XmlTextWriter(stringWriter);

            xmldoc.WriteStartElement("friendinfo");

            xmldoc.WriteAttributeString("ID", ID.ToString());
            xmldoc.WriteAttributeString("Name", Name);
            xmldoc.WriteAttributeString("NoSDFlag", NoSDFlag.ToString());
            xmldoc.WriteAttributeString("Location", Location);
            xmldoc.WriteAttributeString("Instance", Instance);

            xmldoc.WriteEndElement();
            xmldoc.Flush();

            xmldoc.Close();

            stringWriter.Flush();

            result = stringWriter.ToString();

            return result;
        }
    }
}
